import traceback
from io import StringIO
import oss2
from Crypto.SelfTest.Cipher.test_CFB import file_name
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QThread, QTimer, pyqtSignal, QTextStream
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QGridLayout, QFileDialog, QMessageBox
import configparser
import sys
import os
from aliyunsdkcdn.request.v20180510 import RefreshObjectCachesRequest,  PushObjectCacheRequest
from aliyunsdkcore.client import AcsClient
from datetime import datetime, timedelta
from urllib.parse import urlparse
import time
from oss2 import determine_part_size, SizedFileAdapter
from oss2.defaults import multiget_threshold, multipart_threshold, part_size
from oss2.models import PartInfo


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.file_names = None
        self.file_selected_value = None
        self.cdn_selected_value = None
        self.upload_thread = None
        self.file_name = []
        self.file_path = []
        self.progress_value = 0
        self.file_types_value_items = {
            "Ios": "DownLoad/Ios/",
            "Android": "DownLoad/Android/",
            "Windows": "DownLoad/Windows/",
            "Mac": "DownLoad/Mac/",
            "图片": "DownLoad/Picture/"
        }

        self.setWindowTitle('Upload to Aliyun OSS and Refresh CDN')
        self.resize(700, 500)

        self.gridLayout = QGridLayout(self)
        self.setLayout(self.gridLayout)

        self.cdn_label = QtWidgets.QLabel('CDN刷新选项', self)
        self.cdn_combo = QtWidgets.QComboBox(self)
        self.cdn_combo.addItem("预热CDN缓存")
        self.cdn_combo.addItem("刷新CDN缓存")
        self.file_type_label = QtWidgets.QLabel('文件类型', self)
        self.file_type_combo = QtWidgets.QComboBox(self)
        for key, value in self.file_types_value_items.items():
            self.file_type_combo.addItem(key, userData=value)
        self.log_label = QtWidgets.QLabel('详细日志', self)
        self.log_list_view = QtWidgets.QListView(self)
        self.log_list_view.setEditTriggers(QtWidgets.QListView.NoEditTriggers) # 设置日志list只读
        self.log_model = QtGui.QStandardItemModel(self)  # 创建一个QStandardItemModel来存储日志
        self.log_list_view.setModel(self.log_model)  # 将QStandardItemModel设置为QListView的模型
        self.select_Button = QtWidgets.QPushButton('选择文件', self)
        self.select_Button.clicked.connect(self.select_file)
        self.push_Button = QtWidgets.QPushButton('上传文件', self)
        self.push_Button.clicked.connect(self.upload_file)
        self.progress_bar = QtWidgets.QProgressBar(self) # 进度条
        self.progress_bar.setMinimum(0)
        self.progress_bar.setMaximum(100)
        self.progress_bar.setValue(0)

        self.gridLayout.addWidget(self.cdn_label, 0, 0)
        self.gridLayout.addWidget(self.cdn_combo, 0, 1)
        self.gridLayout.addWidget(self.file_type_label, 1, 0)
        self.gridLayout.addWidget(self.file_type_combo, 1, 1)
        self.gridLayout.addWidget(self.log_label, 2, 0)
        self.gridLayout.addWidget(self.log_list_view, 3, 0, 1, 2)
        self.gridLayout.addWidget(self.select_Button, 4, 0)
        self.gridLayout.addWidget(self.push_Button, 4, 1)
        self.gridLayout.addWidget(self.progress_bar, 5, 0 , 1, 2)



    def select_file(self):
        file_dialog = QFileDialog()
        file_dialog.setFileMode(QFileDialog.ExistingFiles)
        # 文件
        file_paths, _ = file_dialog.getOpenFileNames(self, "Select Files", "", "All Files (*)")
        for file_path in file_paths:
            self.file_path.append(file_path)
            self.file_name.append(os.path.basename(file_path))  # 获取文件名
            self.add_log(f'新增文件：{os.path.basename(file_path)} \t {file_path}')


    def upload_file(self):
        if not hasattr(self, 'file_path') or not self.file_path:
            return

        # cdn
        self.cdn_selected_value = self.cdn_combo.currentText()
        # file type
        self.file_selected_value = self.file_types_value_items[self.file_type_combo.currentText()]

        self.push_Button.setEnabled(False)
        self.upload_thread = UploadThread(self.file_path, self.file_name, self.cdn_selected_value, self.file_selected_value)
        self.upload_thread.change_progress.connect(self.change_progress_value)
        self.upload_thread.upload_finished.connect(self.show_result_message)
        self.upload_thread.start(100)


    @classmethod
    def add_log(self, message):
        log_item = QtGui.QStandardItem(message)  # 创建一个QStandardItem来存储日志消息
        window.log_model.appendRow(log_item)  # 将日志消息添加到QStandardItemModel中


    def show_result_message(self, success):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        if success:
            msg.setText("文件上传成功")
            msg.setWindowTitle("成功")
            self.progress_bar.setValue(0)
        else:
            msg.setText("文件上传失败")
            msg.setWindowTitle("失败")
        msg.exec_()
        self.push_Button.setEnabled(True)
        self.file_name = []
        self.file_path = []


    def change_progress_value(self, progress):
        # 设置当前上传进度
        self.progress_bar.setValue(progress)



class UploadThread(QThread):
    upload_finished = pyqtSignal(bool)
    change_progress = pyqtSignal(int)

    def __init__(self, file_path, file_name, cdn_selected_value, file_selected_value):
        super().__init__()
        self.oss_file_path = None
        self.cdn_url = None
        self.file_path = file_path
        self.file_name = file_name
        self.cdn_selected_value = cdn_selected_value
        self.file_selected_value = file_selected_value
        self.oss_access_key_id = None
        self.oss_access_key_secret = None
        self.config = configparser.ConfigParser()

    def run(self):
        # 配置OSS连接
        self.config.read('utils.ini')
        oss_endpoint = 'http://oss-cn-shenzhen.aliyuncs.com'
        self.oss_access_key_id = 'LTAI5tSF9TVhwEw3kk1L9o9K'
        self.oss_access_key_secret = 'fBnWgV4Pzc6a9g3IXhf8Q7LaLRAyNa'
        oss_bucket_name = 'obsbot-script'

        # 创建OSS存储空间连接
        auth = oss2.Auth(self.oss_access_key_id, self.oss_access_key_secret)
        bucket = oss2.Bucket(auth, oss_endpoint, oss_bucket_name)
        print(self.file_name,self.file_path)
        # cdn链接编码

        expiration = int((datetime.utcnow() + timedelta(hours=1)).timestamp())  # URL有效期1小时
        for i in range(len(self.file_name)):
            if self.file_name is not  None:
                print(self.file_name[i])
                expiration_url = bucket.sign_url('GET', self.file_name[i], expiration)
                parsed_url = urlparse(expiration_url)
                self.cdn_url = parsed_url.path
                print("Path:", self.cdn_url)


                # 上传文件
                self.oss_file_path = self.file_selected_value + self.file_name[i]
                try:
                    for a in range(len(self.file_path)):
                        total_size = os.path.getsize(self.file_path[a])
                        part_file_size = determine_part_size(total_size, preferred_size=10*1024*1024)
                        upload_id = bucket.init_multipart_upload(self.oss_file_path).upload_id
                        Window.add_log(f'upload_id: {upload_id}')
                        parts = []
                        with open(self.file_path[a], 'rb') as obj:
                            part_number = 1
                            offset = 0
                            uploaded_file_size = 0
                            while offset < total_size:
                                num_to_upload = min(part_file_size, total_size - offset)
                                # 调用SizedFileAdapter(fileobj, size)方法会生成一个新的文件对象，重新计算起始追加位置。
                                result = bucket.upload_part(self.oss_file_path, upload_id, part_number,
                                                            SizedFileAdapter(obj, num_to_upload))
                                parts.append(PartInfo(part_number, result.etag))

                                offset += num_to_upload
                                part_number += 1
                                uploaded_file_size += num_to_upload # 累加已上传的字节数
                                progress = self.calculate_progress(uploaded_file_size, total_size)  # 计算上传进度百分比
                                self.change_progress.emit(progress)
                            push_result = bucket.complete_multipart_upload(self.oss_file_path, upload_id, parts)
                            QtWidgets.QApplication.processEvents()
                            Window.add_log(f'Oss: {push_result}')
                            # bucket.upload_part(self.file_selected_value)
                    if self.cdn_selected_value == '预热CDN缓存':
                        print(self.cdn_selected_value)
                        self.trigger_cdn_refresh()
                    else:
                        Window.add_log(self.cdn_selected_value)
                        self.trigger_cdn_refresh_caches()
                    self.upload_finished.emit(True)
                except Exception as e:
                    print("文件上传失败：", str(e))
                    self.upload_finished.emit(False)



    def calculate_progress(self, uploaded_file_size, total_size):
        if total_size == 0:
            return 0
        else:
            return int(uploaded_file_size / total_size * 100)



    def trigger_cdn_refresh(self):
        #  CDN client
        Cdn_Client = AcsClient(self.oss_access_key_id, self.oss_access_key_secret, 'cn-shenzhen')

        # 构造请求对象
        request = PushObjectCacheRequest.PushObjectCacheRequest()
        path = self.file_selected_value.rstrip('/')
        request.set_ObjectPath(f'https://resource.obsbothk.com/{path}{self.cdn_url}')

        # 调用预热API
        response = Cdn_Client.do_action_with_exception(request)
        Window.add_log(f'CDN: https://resource.obsbothk.com/{path}{self.cdn_url} \t {response}')


    def trigger_cdn_refresh_caches(self):
        #  CDN client
        Cdn_Caches_Client = AcsClient(self.oss_access_key_id, self.oss_access_key_secret, 'cn-hongkong')

        # 构造请求对象
        request = RefreshObjectCachesRequest.RefreshObjectCachesRequest()
        path = self.file_selected_value.rstrip('/')
        request.set_ObjectPath(f'https://resource.obsbothk.com/{path}{self.cdn_url}')

        # 调用预热API
        response = Cdn_Caches_Client.do_action_with_exception(request)
        Window.add_log(f'CDN: https://resource.obsbothk.com/{path}{self.cdn_url} \t {response}')



if __name__ == '__main__':
    def handle_exception(exc_type, exc_value, exc_traceback):
        traceback.print_exception(exc_type, exc_value, exc_traceback, file=sys.stderr)
    app = QApplication(sys.argv)
    window = Window()
    window.setWindowIcon(QIcon('favicon.ico'))
    window.show()
    sys.excepthook = handle_exception
    sys.exit(app.exec_())
